function Page() { // handles display of the page

  // current page state variables
  this.visible = true;
  this.page = "blank";
  this.loadspeed = 800;
  this.loading = true;
  this.isOkToMove = true;
  
  this.load = function() { // called from <body> onload command

    // handles making the background transparent for nice corners, shadow etc
    window.htmlLoader.paintsDefaultBackground = false;

    // initialises system tray
    systray.init();

    // set min window size initially
    pt = new air.Point;
    pt.y = huddle.minHeight;
    pt.x = huddle.minWidth;
    window.nativeWindow.minSize = pt; // just to initialise this so there's no resizing bugs

    // header
    $("#header>div").append("<h1><a href='http://my.huddle.net'>Huddle</a></h1><span></span><ul><li class='min'>Minimise</li><li class='close'>Close</li></ul>");
    $("#header").show("drop", {direction: "down"}, this.getLoadspeed);
    
    // footer
    $("#footer>div").append("<p></p><h1><a href='http://my.huddle.net'>Huddle</a></h1>");
    $("#footer").show("drop", {direction: "up"}, this.getLoadspeed);

    // handles links in main window
    $("#header h1 a, #footer h1 a, #header ul .min, #header ul .close").mouseout(function() {msgs.hide();});
    
    // huddle link click handlers
    $("#header h1 a, #footer h1 a").click(function(e) {
      e.preventDefault(); // stop it actually opening the link
      air.navigateToURL(new air.URLRequest($(this).attr('href'))); // open link in default browser
    }).mouseover(function() {
      msgs.display("Go to Huddle.net", 2000); // mouseover to display message
      page.isOkToMove = false;
    }).mouseout(function() {
      page.isOkToMove = true;
    });

    // minimise and close button functionality
    $("#header ul .min").click(function() {
      systray.toggle(); // toggles show/hide of window
    }).mouseover(function() {
      msgs.display("Minimise", 2000); // minimise message
    });
    
    $("#header ul .close").click(function() {
      huddle.closeApp(); // calls function to gently exit app
    }).mouseover(function() {
      msgs.display("Exit", 2000);
    });
    
    // minimise and close button move preventer
    $("#header ul .min, #header ul .close").mouseover(function() {
      page.isOkToMove = false;
    }).mouseout(function() {
      page.isOkToMove = true;
    });
    
    // handles moving window
    $("#header").mousedown(function() { page.move(); }, false);
    
    // handles resizing window
    $("#footer span.resize-handle").mousedown(function() {nativeWindow.startResize(air.NativeWindowResize.BOTTOM_RIGHT);});

    // displays login to begin with
    this.displayPage("login",undefined);
    
    // listeners for shift key for select process
    window.addEventListener("keydown", function(event) {
    
      if(event.keyCode == 16) {
        window.isShiftDown = 1;
      }
    
    } );
    
    window.addEventListener("keyup", function(event) {
    
      if(event.keyCode == 16) {
        window.isShiftDown = 0;
      }
    
    } );

    // resize functionality to keep it above min size etc
    window.addEventListener("resize", function (event) {

      newWindowHeight = window.nativeWindow.height;
      newWindowWidth = window.nativeWindow.width;
      
      if ((newWindowHeight <= huddle.minHeight) && (newWindowWidth <= huddle.minWidth)) { // if its too small, resize it back out again
      
          /*
            this bit is in due to a weird quirk in Air,
            which sometimes causes problems if suddenly
            both the width and height go below minimum
          */
      
          pt = new air.Point;
          pt.y = huddle.minHeight;
          pt.x = huddle.minWidth;
          window.nativeWindow.minSize = pt;
          newWindowHeight = huddle.minHeight;
          newWindowWidth = huddle.minWidth;
      
      } else {
      
        if (newWindowHeight < huddle.minHeight) { // minimum height
        
          pt = new air.Point; // make a new air point
          pt.y = huddle.minHeight; // set y
          window.nativeWindow.minSize = pt; // force it onto the window
          newWindowHeight = huddle.minHeight; // change variable for later
        
        }
        
        if (newWindowWidth < huddle.minWidth) { // minimum width

          pt = new air.Point;
          pt.x = huddle.minWidth;
          window.nativeWindow.minSize = pt;
          newWindowWidth = huddle.minWidth;

        }
        
      }

      newHeight = newWindowHeight - 122; // works out how high middle section should be
    
      document.getElementById("huddle").firstChild.firstChild.style.height = newHeight + "px"; // sets height of mid section on resize
    
    } ); 
    
  } // end of load function
  
  // handles moving of the window
  this.move = function() {
  
    if (page.isOkToMove) { // checks its allowed to move
    
      nativeWindow.startMove();
    
    }
    
  }
  
  // determines if content is currently visible
  this.isVisible = function() {
  
    /*
      this exists because using something simple
      like the current state of the container
      div itself makes it a tough job and
      even more complicated in the end
    */
  
    return this.visible;
  
  }
  
  // sets visibility status of the content
  this.setVisible = function(bool) {
  
    if ((bool == true) || (bool == false)) { // little check just to make sure
    
      this.visible = bool;
      
    }
  
  }
  
  // returns the current page
  this.isPage = function() {
  
    return this.page;
  
  }
  
  // sets variable containing page state
  this.setPage = function(newpage) {
  
    this.page = newpage;
  
  }
  
  // returns loading speed of app so its consistent on all effects
  this.getLoadspeed = function() {
  
    return this.loadspeed;
  
  }
  
  // in case you decide to change the speed on the fly
  this.setLoadspeed = function(newSpeed) {
  
    this.loadspeed = newSpeed;
  
  }
  
  // returns whether or not loading is in progress
  this.isLoading = function() {
  
    return this.loading;
  
  }
  
  // sets the loading state and animations
  this.setLoading = function(bool) {
  
    if ((bool == true) || (bool == false)) {
    
      this.loading = bool;
      
      if (bool == true) {
      
        $("#loading").hide().fadeIn();
      
      } else {
      
        $("#loading").fadeOut()
      
      }
      
    }
  
  }
  
  // displays a set of content
  this.displayPage = function(type,id) {

    this.setLoading(true); // sets loading status
    
    selecter.wipe(); // erases any selections currently or previously made
  
    if (this.isVisible()) { // if visible flag is set
    
      $("#huddle>div>div *").fadeOut(this.getLoadspeed()/2); // fade out contents
      this.setVisible(false); // set visible flag to false

    }
    
    // switches content after timeout (to allow for fade)
    setTimeout(function() {page.switchContent(type,id);}, this.getLoadspeed()/2); 
  
  }
  
  // changes the content inside the window
  this.switchContent = function(type,id) {
  
    this.setPage(type); // sets the new page type
    
    this.empty(); // empties the div
    
    // sorts type and ID in case it is unknown
    sorted = [];
    sorted = sortTypeId(type,id);
    type = sorted[0];
    id = sorted[1];
    
    folder.set(0); // sets current folder to 0, will be change to valid ID if folder is loaded
    
    /*
      this code and drag.set() in switch case "folder" is commented
      to disable the ability to drag and drop files onto the background
      of a folder, this was a bit broken when the new UI was implemented
      and will need a bit of fixing before it works again
    */
    //drag.remove("huddle");
    
    switch(type) {

      case "workspace":
        workspace.get(id);
        whatsnew.get(id); // gets whats new feed for workspace / dashboard
        breadcrumbs.hide(); // don't want breadcrumbs on workspaces so hide
        break;
        
      case "folder":
        folder.get(id);
        folder.set(id); // sets current folder id for use in drag and drop etc
        //drag.set("huddle"); // see above block comment
        break;
        
      case "file":
        getFile(id);
        break;
        
      case "login":
        login.get(id);
        break;
        
      case "upload":
        upload.get(id);
        break;
        
      case "blank":
        break;
        
      default:
        msgs.setError(1,"No type specified in switchContent call","Unable to find page",0);
        break;

    }
  
  }
  
  // called when loading of page is complete
  this.loadComplete = function() {
  
    if (!page.isVisible()) { // checks its not visible first, to make sure there's no problems with jQuery effects

      $("#huddle>div>div *").hide().fadeIn(page.getLoadspeed()/2); // fades in content
      page.setVisible(true); // sets visibility flag
    
    }
    
    page.setLoading(false); // hides loading animation
  
  }
  
  // quick method to empty the contents easily
  this.empty = function() {
  
    $("#huddle>div>div").empty();
  
  }

}